package com.pipe.protocol.events.write;

import com.pipe.exception.ColumnReaderException;
import com.pipe.protocol.events.table.TableMapEvent;
import com.pipe.protocol.events.table.reader.ColumnType;
import com.pipe.protocol.query.TableMetaInfo;
import com.pipe.util.IntegerConveter;
import com.pipe.util.MyBytebufUtil;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @author Kiven
 */
public class WriteRowEventV2Rows {
    private static Logger LOG = LoggerFactory.getLogger(WriteRowEventV2Rows.class);
    private String tableName;
    private List<Row> rows;

    public WriteRowEventV2Rows(String tableName, List<Row> rows) {
        this.tableName = tableName;
        this.rows = rows;
    }

    public static WriteRowEventV2Rows build(ByteBuf byteBuf, int columnNum, TableMapEvent tableMapEvent, Map<String, TableMetaInfo> tableMetaInfoMap, boolean isUpdate) throws ColumnReaderException {
        List<Row> rows = new LinkedList<>();
        ColumnType[] columnTypeDef = tableMapEvent.getPayLoad().getColumnTypeDef();
        String tableName = tableMapEvent.getPayLoad().getTableName();
        TableMetaInfo metaInfo = tableMetaInfoMap.get(tableName);
        while (byteBuf.readableBytes()!=0){
            Row row = Row.build(byteBuf, columnNum, columnTypeDef, metaInfo, isUpdate);
            rows.add(row);
            MyBytebufUtil.printBytebuf(byteBuf);
        }
        return new WriteRowEventV2Rows(tableName, rows);
    }

    public static class Row{
        private byte[] nulBitmap;
        private List<Object> values;
        private byte[] updateNulBitmap;
        private List<Object> updateValues;

        public Row(byte[] nulBitmap, List<Object> values, byte[] updateNulBitmap, List<Object> updateValues) {
            this.nulBitmap = nulBitmap;
            this.values = values;
            this.updateNulBitmap = updateNulBitmap;
            this.updateValues = updateValues;
        }

        public static Row build(ByteBuf byteBuf, int columnNum, ColumnType[] columnTypeDef, TableMetaInfo tableMetaInfo, boolean isUpdate) throws ColumnReaderException {
            byte[] nulBitmap = MyBytebufUtil.readByteFromBytebuf(byteBuf, (int)((columnNum + 7)/8) );
            byte[] bitArrOfnulBitMap = IntegerConveter.getBitArrayFromByteArr(nulBitmap);
            List<Object> values = new LinkedList<>();
            for (int i = 0; i < columnTypeDef.length; i++) {
                if (bitArrOfnulBitMap[bitArrOfnulBitMap.length - 1 - i] == 1){
                    LOG.debug("[WriteRowEventV2] this column: {} has null value!", tableMetaInfo.getResultList().get(i));
                    values.add(null);
                    continue;
                }
                ColumnType ct = columnTypeDef[i];
                MyBytebufUtil.printBytebuf(byteBuf);
                values.add(ct.getColumnReader().read(byteBuf, tableMetaInfo.getColumnLengthList().get(i)));
            }
            byte[] updateNulBitmap = null;
            List<Object> updateValues = null;
            if (isUpdate){
                updateNulBitmap = MyBytebufUtil.readByteFromBytebuf(byteBuf, (int)((columnNum + 7)/8) );
                byte[] bitArrOfupdateNulBitmap = IntegerConveter.getBitArrayFromByteArr(updateNulBitmap);
                updateValues = new LinkedList<>();
                for (int i = 0; i < columnTypeDef.length; i++) {
                    if (bitArrOfupdateNulBitmap[bitArrOfupdateNulBitmap.length - 1 - i] == 1){
                        LOG.debug("[WriteRowEventV2] - [update] this column: {} has null value!", tableMetaInfo.getColumnDefinitionList().get(i));
                        updateValues.add(null);
                        continue;
                    }
                    ColumnType ct = columnTypeDef[i];
                    updateValues.add(ct.getColumnReader().read(byteBuf, tableMetaInfo.getColumnLengthList().get(i)));
                }
            }
            return new Row(nulBitmap, values, updateNulBitmap, updateValues);
        }

        public List<Object> getValues() {
            return values;
        }

        public List<Object> getUpdateValues() {
            return updateValues;
        }

        @Override
        public String toString() {
            return "Row{" +
                    "nulBitmap=" + Arrays.toString(nulBitmap) +
                    ", values=" + values +
                    ", updateNulBitmap=" + Arrays.toString(updateNulBitmap) +
                    ", updateValues=" + updateValues +
                    '}';
        }
    }

    public String getTableName() {
        return tableName;
    }

    public List<Row> getRows() {
        return rows;
    }

    @Override
    public String toString() {
        return "WriteRowEventV2Rows{" +
                "tableName='" + tableName + '\'' +
                ", rows=" + rows +
                '}';
    }
}